package cn.iocoder.yudao.module.wms.service.warehouse;

import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.base.api.ProductWarehouseApi;
import cn.iocoder.yudao.module.base.api.dto.ProductWarehouseRespDTO;
import cn.iocoder.yudao.module.wms.dal.dataobject.warehouse.PositionDO;
import cn.iocoder.yudao.module.wms.dal.mysql.warehouse.PositionMapper;
import cn.iocoder.yudao.module.wms.enums.warehouse.OpenPositionEnum;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import cn.iocoder.yudao.module.wms.controller.admin.warehouse.vo.*;
import cn.iocoder.yudao.module.wms.dal.dataobject.warehouse.WarehouseDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.wms.dal.mysql.warehouse.WarehouseMapper;

import javax.annotation.Resource;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.wms.enums.ErrorCodeConstants.*;

/**
 * 仓库 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class WarehouseServiceImpl implements WarehouseService {

    @Resource
    private WarehouseMapper warehouseMapper;
    @Resource
    private PositionMapper positionMapper;
    @Resource
    private ProductWarehouseApi productWarehouseApi;

    @Override
    public Long createWarehouse(WarehouseSaveReqVO createReqVO) {
        validateWarehouseCodeExists(null, createReqVO.getCode());
        // 插入
        WarehouseDO warehouse = BeanUtils.toBean(createReqVO, WarehouseDO.class);
        warehouseMapper.insert(warehouse);
        // 返回
        return warehouse.getId();
    }

    @Override
    public void updateWarehouse(WarehouseSaveReqVO updateReqVO) {
        // 校验存在
        validateWarehouseExists(updateReqVO.getId());
        // 校验CODE重复
        validateWarehouseCodeExists(updateReqVO.getId(), updateReqVO.getCode());
        // 如果库位关闭，则不允许仓库下存在库位信息
        if (updateReqVO.getOpenPosition().equals(CommonStatusEnum.DISABLE.getStatus())) {
            validatePositionExistsByWarehouse(updateReqVO.getId());
        }
        // 更新
        WarehouseDO updateObj = BeanUtils.toBean(updateReqVO, WarehouseDO.class);
        warehouseMapper.updateById(updateObj);
    }

    @Override
    public void deleteWarehouse(Long id) {
        // 校验存在
        WarehouseDO warehouse = validateWarehouseExists(id);
        // 判断仓库是否被产品绑定
        validateWarehouseBindProduct(id);
        // TODO 判断仓库下是否有库存信息
        // 判断仓库下是否有库位信息
        validatePositionExistsByWarehouse(id);
        // 删除
        warehouseMapper.deleteById(id);
    }

    private WarehouseDO validateWarehouseExists(Long id) {
        WarehouseDO warehouseDO = warehouseMapper.selectById(id);
        if (warehouseDO == null) {
            throw exception(WAREHOUSE_NOT_EXISTS);
        }
        return warehouseDO;
    }
    private void validateWarehouseCodeExists(Long id, String code) {
        WarehouseDO warehouseDO = warehouseMapper.selectByCode(code);
        if (ObjectUtil.isEmpty(warehouseDO)) {
            return;
        }
        if (id == null) {
            throw exception(WAREHOUSE_CODE_EXISTS);
        }
        if (!id.equals(warehouseDO.getId())) {
            throw exception(WAREHOUSE_CODE_EXISTS);
        }
    }

    private void  validateWarehouseBindProduct(Long id) {
        ProductWarehouseRespDTO in = productWarehouseApi.getFirstProductWarehouseByInWarehouse(id);
        if (ObjectUtil.isNotEmpty(in)) {
            throw exception(WAREHOUSE_BIND_PRODUCT);
        }
        ProductWarehouseRespDTO out = productWarehouseApi.getFirstProductWarehouseByOutWarehouse(id);
        if (ObjectUtil.isNotEmpty(out)) {
            throw exception(WAREHOUSE_BIND_PRODUCT);
        }
    }
    private void validatePositionExistsByWarehouse(Long id) {
        PositionDO position = this.getFirstPositionByWarehouse(id);
        if (ObjectUtil.isNotEmpty(position)) {
            throw exception(WAREHOUSE_BIND_POSITION);
        }
    }

    @Override
    public WarehouseDO getWarehouse(Long id) {
        return validateWarehouseExists(id);
    }

    @Override
    public PageResult<WarehouseDO> getWarehousePage(WarehousePageReqVO pageReqVO) {
        return warehouseMapper.selectPage(pageReqVO);
    }

    @Override
    public List<WarehouseDO> getWarehouseList() {
        return warehouseMapper.selectList();
    }

    @Override
    public List<WarehouseDO> getWarehouseListById(Collection<Long> ids) {
        return warehouseMapper.selectList(ids);
    }

    // ==================== 子表（库位） ====================

    @Override
    public PageResult<PositionDO> getPositionPage(PageParam pageReqVO, Long warehouseId) {
        return positionMapper.selectPage(pageReqVO, warehouseId);
    }

    @Override
    public Long createPosition(PositionDO position) {
        validateOpenPosition(position.getWarehouseId());
        validatePositionCodeExists(null, position.getCode(), position.getWarehouseId());
        positionMapper.insert(position);
        return position.getId();
    }

    @Override
    public void updatePosition(PositionDO position) {
        validateOpenPosition(position.getWarehouseId());
        // 校验存在
        validatePositionExists(position.getId());
        // 校验CODE
        validatePositionCodeExists(position.getId(), position.getCode(), position.getWarehouseId());
        // 更新
        positionMapper.updateById(position);
    }

    @Override
    public void deletePosition(Long id) {
        // 校验存在
        validatePositionExists(id);
        // TODO 校验当前库位下是否有存货
        // 校验库位是否被产品绑定
        validatePositionBindProduct(id);
        // 删除
        positionMapper.deleteById(id);
    }

    @Override
    public PositionDO getPosition(Long id) {
        return validatePositionExists(id);
    }

    @Override
    public List<PositionDO> getPositionList(Collection<Long> warehouseIds) {
        return positionMapper.selectListByWarehouseId(warehouseIds);
    }

    @Override
    public List<PositionDO> getPositionListById(Collection<Long> ids) {
        return positionMapper.selectList(ids);
    }

    private PositionDO getFirstPositionByWarehouse(Long id) {
        return positionMapper.selectFirstByWarehouse(id);
    }

    private PositionDO validatePositionExists(Long id) {
        PositionDO position = positionMapper.selectById(id);
        if (ObjectUtil.isEmpty(position)) {
            throw exception(POSITION_NOT_EXISTS);
        }
        return position;
    }

    private void validatePositionBindProduct(Long id) {
        ProductWarehouseRespDTO in = productWarehouseApi.getFirstProductWarehouseByInPosition(id);
        if (ObjectUtil.isNotEmpty(in)) {
            throw exception(POSITION_BIND_PRODUCT);
        }
        ProductWarehouseRespDTO out = productWarehouseApi.getFirstProductWarehouseByOutPosition(id);
        if (ObjectUtil.isNotEmpty(out)) {
            throw exception(POSITION_BIND_PRODUCT);
        }
    }

    private void validatePositionCodeExists(Long id, String code, Long warehouseId) {
        PositionDO positionDO = positionMapper.selectByCode(warehouseId, code);
        if (ObjectUtil.isEmpty(positionDO)) {
            return;
        }
        if (id == null) {
            throw exception(POSITION_CODE_EXISTS);
        }
        if (!id.equals(positionDO.getId())) {
            throw exception(POSITION_CODE_EXISTS);
        }
    }

    private void validateOpenPosition(Long warehouseId) {
        WarehouseDO warehouseDO = this.validateWarehouseExists(warehouseId);
        if (warehouseDO.getOpenPosition().equals(OpenPositionEnum.NO.getStatus())) {
            throw exception(POSITION_NOT_CHANGE);
        }
    }


    private void deletePositionByWarehouseId(Long warehouseId) {

        positionMapper.deleteByWarehouseId(warehouseId);
    }

}